/*
 * Copyright (c) 2014 pkzd/1.0, 颜文泽(robin)
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     1 Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     2 Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include	<ide.h>
#include	<pic.h>
#include	<asm.h>
#include	<conf.h>
#include	<mount.h>
#include	<param.h>

uint ide_two;
uint ide_slv;
ushort ide_cmd;
ushort ide_ctrl;
uint ide0_sectors;
uint ide1_sectors;
struct devtab idetab;

int
ide_init(void)
{
	ide_two = *(uint *)IDE_TWO;
	ide_slv	= *(uint *)IDE_SLV;
	ide_cmd = *(ushort *)IDE_CMD;
	ide_ctrl = *(ushort *)IDE_CTRL;
	ide0_sectors = *(uint *)(IDE0_PARAM + IDE_SIZ_OFF);
	ide1_sectors = *(uint *)(IDE1_PARAM + IDE_SIZ_OFF);
	pic_eblirq(FOL_IRQ);
	pic_eblirq(IDE_IRQ);
	return 0;
}

int
ideopen(dev_t dev)
{
	return 0;
}

int
ideclose(dev_t dev)
{
	return 0;
}

int
iderdwr(struct buf *bp)
{
	struct devtab *dp;

	bp->av_forw = NULL;	
	dp = bdevsw[MAJOR(bp->b_dev)].d_tab;
	if(dp->d_actf == NULL){
		dp->d_actf = bp;
	}else
		dp->d_actl->av_forw = bp; 
	dp->d_actl = bp;
	if(dp->d_active == 0) 
		idestart();	
	return 0; 
}

void
idestart(void)
{
	uint s, bn;
	struct buf *bp;
	struct devtab *dp;

	dp = bdevsw[MAJOR(rootdev)].d_tab;
	if(!(bp = dp->d_actf)) 
		return;
	dp->d_active++;
	bn = bp->b_blkno * 2;
	s = MINOR(bp->b_dev) ? ide_slv : 0; 
	while((inb(ide_cmd + 7) & IDE_MASK) != IDE_DRDY)
                ;
        outb(bp->b_ide.h_cnt, ide_cmd + 2);
        outb((uchar)bn, ide_cmd + 3);
        outb((uchar)(bn >> 8), ide_cmd + 4);
        outb((uchar)(bn >> 16), ide_cmd + 5);
        outb((uchar)((bn >> 24) | 0xe0 | s), ide_cmd + 6);
        outb(bp->b_flags & B_READ ? IDE_READ : IDE_WRITE, ide_cmd + 7);
	if(bp->b_flags & B_WRITE)
		outsw(bp->b_ide.h_addr, ide_cmd, SECTORSIZ >> 1);	
}

void
ideint(void)
{
	uint x;
	struct buf *bp;
	struct devtab *dp;

	dp = bdevsw[MAJOR(rootdev)].d_tab;
	if(dp->d_active == 0)
		return;
	bp = dp->d_actf;
	x = inb(ide_cmd + 7);
	if(bp->b_flags & B_WRITE){
		bp->b_ide.h_cnt--;
		bp->b_ide.h_addr += SECTORSIZ;
		if(bp->b_ide.h_cnt)
			outsw(bp->b_ide.h_addr, ide_cmd, SECTORSIZ >> 1);	
	}else{
		if((x & (IDE_BUSY | IDE_DRQ)) == IDE_DRQ)
			insw(bp->b_ide.h_addr, ide_cmd, SECTORSIZ >> 1);
		bp->b_ide.h_cnt--;
		bp->b_ide.h_addr += SECTORSIZ;
	}
	if(x & IDE_ERR)
		bp->b_flags |= B_ERROR;
	if(bp->b_ide.h_cnt == 0){
		dp->d_active--;
		dp->d_actf = bp->av_forw;
		iodone(bp);
		idestart();
	}
}
